#include "rttSerial.h"

#include <unistd.h>
#include <poll.h>
#include <termios.h>
#include <string.h>

using namespace NXMFC;

rttSerial::rttSerial() : fd(-1)
{
}

rttSerial::~rttSerial()
{
}

bool rttSerial::open(const char *name)
{
    fd = ::open(name, O_RDWR);
    if (fd < 0)
        return false;

    return true;
}

void rttSerial::close()
{
    if (fd >= 0)
        ::close(fd);
}

bool rttSerial::waitForReadyRead(int ms)
{
    struct pollfd pf;

    pf.fd = fd;
    pf.events = POLLIN;

    if (::poll(&pf, 1, ms) <= 0)
        return false;

    if (pf.revents & POLLIN)
        return true;

    return false;
}

bool rttSerial::waitForReadyWrite(int ms)
{
    struct pollfd pf;

    pf.fd = fd;
    pf.events = POLLOUT;

    if (::poll(&pf, 1, ms) <= 0)
        return false;

    if (pf.revents & POLLOUT)
        return true;

    return false;
}

int rttSerial::read(void *buf, unsigned max)
{
    return ::read(fd, buf, max);
}

int rttSerial::readAll(void *buf, unsigned max, int timeout_ms)
{
    int len = 0;
    uint8_t *p = (uint8_t *)buf;

    while (max)
    {
        int ret;

        if (!waitForReadyRead(timeout_ms))
            break;

        ret = read(p, max);
        if (ret <= 0)
            break;

        p += ret;
        max -= ret;
        len += ret;
    }

    return len;
}

int rttSerial::write(const void *buf, unsigned max)
{
    return ::write(fd, buf, max);
}

int rttSerial::write(const char *data)
{
    return writeAll(data, strlen(data));
}

int rttSerial::writeAll(const void *buf, unsigned max, int timeout_ms)
{
    int len = 0;
    uint8_t *p = (uint8_t *)buf;

    while (max)
    {
        int ret;

        if (!waitForReadyWrite(timeout_ms))
            break;

        ret = write(p, max);
        if (ret <= 0)
            break;

        p += ret;
        max -= ret;
        len += ret;
    }

    return len;
}

bool rttSerial::setup(int br, int parity, int stopbits)
{
    struct termios tem;

    if (tcgetattr(fd, &tem) != 0)
        return false;

    switch (br)
    {
    case 9600:
    {
        cfsetospeed(&tem, B9600);
        cfsetispeed(&tem, B9600);
    }
    break;
    case 19200:
    {
        cfsetospeed(&tem, B19200);
        cfsetispeed(&tem, B19200);
    }
    break;
    case 38400:
    {
        cfsetospeed(&tem, B38400);
        cfsetispeed(&tem, B38400);
    }
    break;
    case 115200:
    {
        cfsetospeed(&tem, B115200);
        cfsetispeed(&tem, B115200);
    }
    break;
    default:
    {
        cfsetospeed(&tem, CBAUDEX);
        cfsetispeed(&tem, CBAUDEX);

        tem.__c_ispeed = br;
        tem.__c_ospeed = br;
    }
    break;
    }

    tem.c_oflag &= ~OPOST;
    if (tcsetattr(fd, TCSANOW, &tem) != 0)
        return false;

    return true;
}

bool rttSerial::setBaudRate(int br)
{
    return setup(br);
}

bool rttSerial::clear(enum Direction dir)
{
    int ret = -1;

    switch (dir)
    {
    case Input:
        ret = tcflush(fd, TCIFLUSH);
        break;
    case Output:
        ret = tcflush(fd, TCOFLUSH);
        break;
    case AllDirections:
        ret = tcflush(fd, TCIOFLUSH);
        break;
    }

    return (ret == 0);
}

bool rttSerial::setHalfDuplex(bool en)
{
    int ret;

    ret = ::ioctl(fd, TIOCSERHD, (void*)en);

    return (ret == 0);
}
